package logic;

/**
 * class representing single player engine
 * @author Rekin
 *
 */
public class EngineSinglePlayer extends Engine{
	
	/**
	 * @param temp user settings used for engine creation
	 */
	public EngineSinglePlayer(TempUserSettings temp){
		super(temp);
		canSave=false;
		initPlayers();
	}
	
	/**
	 * handling computer players movement
	 * @param player player to move
	 */
	private void makeOtherPlayersMove(int player){
		if(players[player].isBankrupt()){
			return;
		}
		if(!players[player].isInGame()){
			return;
		}
		if(onePlayerLeft()>0){
			return;
		}
		
		if(!players[player].isMoved() || (players[player].isMoved() && players[player].getGivenStake()<currentStake) ){
			players[player].cardSort();
			players[player].preCountCards();
			if(currentStake>defaultStake){ //double stake
				if(enteringGameAlgorithmWithDouble(player)){
					int stakeToGive = currentStake-players[player].getGivenStake();
					manageMainPlayer(player,stakeToGive);
				}
				else{
					players[player].setInGame(false);
				}
			}
			else{ // single stake
				if(enteringGameAlgorithm(player)){
					if(currentRound==2 && enteringGameAlgorithmWithDouble(player)){
						currentStake*=2;
						int stakeToGive = currentStake-players[player].getGivenStake();
						manageMainPlayer(player, stakeToGive);
					}
					else{
						int stakeToGive = currentStake-players[player].getGivenStake();
						players[player].setGivenStake(stakeToGive);
						manageMainPlayer(player, stakeToGive);
					}
				}
				else{
					players[player].setInGame(false);
				}
			}
		}
		players[player].setMoved(true);
	}
	
	/**
	 * logic for entering game
	 * @param player player to move
	 * @return true if player should enter the game else false
	 */
	private boolean enteringGameAlgorithm(int player){
		if((players[player].getSequenceKind()>0) || 
					(players[player].getSequenceKind()==0 && 
					players[player].getMoney()>=4*currentStake)){
			return true;
		}
		return false; //don't enter
	}
	
	/**
	 * logic for entering game with double stake
	 * @param player player to move
	 * @return true if player should enter the game else false
	 */
	private boolean enteringGameAlgorithmWithDouble(int player){
		if((players[player].getSequenceKind()>1)){
			return true;
		}
		return false; //don't enter
	}
	
	/**
	 * handling computer players exchange cards
	 * @param player player to move
	 */
	private void makeOtherPlayersExchange(int player){
		if(players[player].isMoved())
			return;
		if(players[player].isBankrupt())
			return;
		if(!players[player].isInGame()) 
			return;
		
		players[player].cardSort();
		players[player].preCountCards();  //find current sequences

		boolean tab[]=players[player].getSelectedCards(); 
		for(int a=0;a<tab.length;++a){
			//reverse selection because selected where those from sequence
			players[player].getCard(a).setSelected(!tab[a]);  
		}
		players[player].exchangeCards(cardDeck);
		
		players[player].cardSort();
		players[player].countCards();  //we know who has what
		
		players[player].setMoved(true);
		
		players[player].setGivenStake(0); //resets given stake for next round
	}
	
	/**
	 * handles computer moves before human player
	 */
	private void handleBeforePlayerMoves(){
		for (int i = playerToMove; i < playerNumber; ++i) {
			if (i==0) { //human player is first to move in the round
				return;
			}
			if(currentRound==1)
				makeOtherPlayersExchange(i);
			else
				makeOtherPlayersMove(i);
		}
	}
	
	/**
	 * handles computer moves after human player
	 */
	private void handleAfterPlayerMoves(){
		for (int i = 1; i < playerNumber; ++i) {
			if(i==playerToMove){
				break;
			}
			if(currentRound==1)
				makeOtherPlayersExchange(i);
			else
				makeOtherPlayersMove(i);
		}
	}
	
	/**
	 * logic of game depending on a human action (button hit)
	 * @param pressedButton button that was pressed by player
	 */
	public void doLogic(int pressedButton){
		switch(pressedButton){
		case LogicHelper.pressButtonCall:
			callButton();
			break;
		case LogicHelper.pressButtonExchange:
			exchangeButton();
			break;
		case LogicHelper.pressButtonDouble:
			doubleButton();
			break;
		case LogicHelper.pressButtonDrop:
			dropButton();
			break;
		case LogicHelper.pressButtonNext:
			nextButton();
			break;
		default:
			break;
		}
	}
	
	/**
	 * call button event handling
	 */
	private void callButton(){
		if(currentRound==0){ //call . drop
			manageMainPlayer(0, currentStake);
		}
		else if(currentRound==2){ //call . double drop
			manageMainPlayer(0, currentStake);
		}
		else if(currentRound==3){ //call . drop - special when computer double after player
			players[0].setMoved(true);
			//stake is divided by 2 because this player has already given the half of the stake in round 2
			players[0].substractMoney(currentStake/2);
			players[0].setGivenStake(players[0].getGivenStake()+currentStake/2);
			moneyOnBoard+=currentStake/2;
		}
		nextSubRoundEvent();
	}
	
	/**
	 * double button event handling
	 */
	private void doubleButton(){ //call double drop  -only round 2
		currentStake*=2;
		manageMainPlayer(0, currentStake);
		nextSubRoundEvent();
	}
	
	/**
	 * exchange button event handling
	 */
	private void exchangeButton(){ //call exchange drop  -only round 1
		players[0].exchangeCards(cardDeck);
		players[0].setGivenStake(0);
		nextSubRoundEvent();
	}
	
	/**
	 * drop button event handling
	 */
	private void dropButton(){
		players[0].setInGame(false);
		players[0].setMoved(true);
		//drop logic for 0 and 2 round
		//we don't do anything special in the 3rd round
		if(currentRound==0){
			if(onePlayerLeft()>0){
				currentRound=3; //when there is only one computer player left go to end round 
										//- he will be counted as winner automatically
			}
			else{
				handleAfterPlayerMoves(); //for round 0
				if(onePlayerLeft()>0){
					currentRound=3; //when there is only one computer player left go to end round 
											//- he will be counted as winner automatically
				}
				else{
					++currentRound;
					setPlayersNotMoved();
					//exchange round
					handleBeforePlayerMoves();
					handleAfterPlayerMoves();
					++currentRound;
					//now go to round 2
					setPlayersNotMoved();
					handleBeforePlayerMoves(); //moves before player in round 2
					//moves after are handles in next if statement
				}
			}
		}
		//else is ommited on purpose, because after leave first if we will go to round 2
		if(currentRound==2){
			if(onePlayerLeft()>0){
				currentRound=3; //when there is only one computer player left go to end round 
										//- he will be counted as winner automatically
			}
			else{
				handleAfterPlayerMoves(); //to handle moves after player
				if(onePlayerLeft()>0){
					currentRound=3; //when there is only one computer player left go to end round 
											//- he will be counted as winner automatically
				}
				setPlayersNotMoved();
			}
			++currentRound;
			if(currentRound==3){
				if(!hasAnyoneGivenDoubleStake()){
					//do nothing
				}
				else{
					//check all players have given double or dropped
					handleBeforePlayerMoves();
					handleAfterPlayerMoves();
				}
				++currentRound;
			}
			if(currentRound==4){ //final round
				sumUpThisRound(); //count who won etc
				return;
			}
		}
		currentRound=3;
		nextSubRoundEvent();
	}
	
	/**
	 * next button event handling
	 */
	private void nextButton(){
		canSave=true;
		MainMidlet.getMidletInstance().stats.updateStatistics( roundWinner==0 ? true : false );
		initCardDeck();
		for(int i=0;i<players.length;++i){
			if(players[i].isBankrupt())
				continue;
			players[i].resetPlayerForNewRound();
			players[i].initCards(cardDeck);
		}
		if(roundWinner>=0){
			players[roundWinner].addMoney(moneyOnBoard);
			moneyOnBoard=0;
		}
		do{
		++playerToMove;
		playerToMove%=playerNumber;
		}
		while(players[playerToMove].isBankrupt());
		
		currentRound=0;
		currentStake=defaultStake;
		takeEntryFee();
		
		checkBankrupcyCondition();
		checkVictoryCondition();
		checkDefeatCondition();
		
		handleBeforePlayerMoves();
		if(checkComputersDropCondition()){
			currentRound=4; //maybe computers left after players
			sumUpThisRound(); //count who won etc
		}
	}
	
	/**
	 * method for summing up round
	 */
	private void sumUpThisRound(){
		for(int i=0;i<players.length;++i){ //just to be sure they have their cards counted
			if(players[i].isInGame() && !players[i].isBankrupt()){
				players[i].cardSort();
				players[i].countCards();
			}
		}
		roundWinner=whoWon();
		if(roundWinner>=0){
			winnerName=players[roundWinner].getName();
		}
		else{
			winnerName="DRAW";
		}
	}
	
	/**
	 * manages player entering game
	 * @param num player index
	 * @param money money that is given when entering game
	 */
	private void manageMainPlayer(int num, int money){
		players[num].setMoved(true);
		players[num].substractMoney(money);
		players[num].setGivenStake(money);
		moneyOnBoard+=money;
	}
	
	/**
	 * handles next sub-events
	 */
	private void nextSubRoundEvent(){
		handleAfterPlayerMoves(); //to handle moves after player
		if(checkComputersDropCondition()){
			currentRound=3; //maybe computers left after players
		}
		++currentRound;
		if(currentRound==3){
			if(!hasAnyoneGivenDoubleStake()){
				//if no double was hit by anyone go to next round
				++currentRound;
			}
			else{
				handleBeforePlayerMoves(); 
				boolean shouldGoToNextRound = false;
				if(players[0].getGivenStake()>Engine.defaultStake){
					shouldGoToNextRound=true;  //player gave the extra money, so move last computers and go on to final round
					handleAfterPlayerMoves();
				}
				if(shouldGoToNextRound){
					++currentRound;
				}
			}
		}
		if(currentRound==4){ //final round
			sumUpThisRound(); //count who won etc
			return;
		}
		setPlayersNotMoved();
		handleBeforePlayerMoves();
		
		if(checkComputersDropCondition()){
			currentRound=4; //maybe computers left after players
			sumUpThisRound(); //count who won etc
		}
	}
	
	/**
	 * checks if only one player is in game
	 * @return player that is only one in game or -1 when there are more players still in game
	 */
	private int onePlayerLeft() {
		int playersInGame = 0;
		for(int i=0;i<players.length;++i){
			if(players[i].isInGame()){
				++playersInGame;
			}
		}
		if(playersInGame==1){
			for(int i=0;i<players.length;++i){
				if(players[i].isInGame()){
					return i;
				}
			}
		}
		return -1;
	}
	
	/**
	 * checks if computer players have dropped the game
	 * @return true if computers left or false
	 */
	private boolean checkComputersDropCondition(){
		int droppedPlayers = 0;
		for (int i = 1; i < players.length; i++) {
			if(!players[i].isInGame()){
				++droppedPlayers;
			}
		}
		return players.length-droppedPlayers == 1 ? true : false;
	}
	
	/**
	 * check player victory condition
	 */
	private void checkVictoryCondition(){
		for (int i = 1; i < players.length; i++) {
			if(!players[i].isBankrupt()){
				totalGameVictory = false;
				return;
			}
		}
		totalGameVictory = true;
	}
	
	/**
	 * check player defeat condition
	 */
	private void checkDefeatCondition(){
		if(players[0].isBankrupt()){
			totalGameDefeat = true;
		}
		else{
			totalGameDefeat = false;
		}
	}
	
	/**
	 * check players bancrupcy condition
	 */
	public void checkBankrupcyCondition(){
		for (int i = 0; i < players.length; i++) {
			if(players[i].getMoney()<=0){
				players[i].setBankrupt(true);
			}
		}
	}
	
	/**
	 * check current round stake (if anyone gave double stake)
	 * @return true if current stake is double otherwise false
	 */
	private boolean hasAnyoneGivenDoubleStake(){
		for (int i = 0; i < players.length; i++) {
			if(players[i].getGivenStake()>defaultStake){
				return true;
			}
		}
		return false;
	}
	
	/**
	 * sets players' flag moved to false after sub-round complete
	 */
	private void setPlayersNotMoved(){
		for(int i=0;i<playerNumber;++i){
			players[i].setMoved(false);
		}
	}
	
	/**
	 * @Override
	 * initiates players for single player
	 */
	protected void initPlayers(){
		players=new Player[playerNumber];
		for(int i=0;i<playerNumber;++i){
			players[i]=new Player(getStartingMoney());
			players[i].setName("Player"+(i+1));
			players[i].initCards(cardDeck);
		}
		players[0].setName(predefinedSettings.getPlayerName());
	}
	
}
